#include "PLBlob.h"
#include "PLVertex.h"
#include "PLVec2.h"

#include <iostream>

using std::cout;
using std::endl;
using std::multiset;
using std::set;
using std::vector;

const PLuint PL_MODELFORMAT_OBJ_INDEX_OFFSET = 1U;

const PLuint U = 0U;
const PLuint V = 1U;

// OpenCV stores the colors in BGR-order
const PLuint RED = 2U;
const PLuint GREEN = 1U;
const PLuint BLUE = 0U;

PLuint PLBlob::numBlobs = 0;
PLuint PLBlob::runningIDs = 0;

/**
  *	Constructs a new empty PLBlob object with a unique ID
  */
PLBlob::PLBlob( void )
{
	numBlobs++;

	mID = runningIDs;

	runningIDs++;
}

/**
  *	Destroys the PLBlob object. The PLTriangle objects (with all the PLEdge objects)
  * are not destroyed. Call DeleteData() prior to the deletion of the PLBlob object
  * to do that.
  */
PLBlob::~PLBlob( void )
{
	// Delete the vectors with the edge paths
	for (vector<vector<PLEdge*>*>::iterator vector_it = mOutlineEdgePaths.begin();
		vector_it != mOutlineEdgePaths.end(); vector_it++)
	{
		delete *vector_it;
	}

	numBlobs--;
}

/**
  *	Adds a single outline edge to the PLBlob object. An outline edge can
  * also be part of a hole inside the PLBlob region. The edge is not copied
  * but added directly, so don't delete this PLEdge object after insertion!
  *
  * @param edge pointer to the PLEdge object that is added to the outline edges
  */
void PLBlob::AddOutlineEdge( PLEdge *edge )
{
	mOutlineEdgeSet.insert( edge );
}

/**
  *	Adds a single triangle to the PLBlob.
  *
  * @param triangle pointer to the PLTriangle object that is added to the PLBlob region
  */
void PLBlob::AddTriangle( PLTriangle *triangle )
{
	mTriangles.push_back( triangle );
}

/**
  *	Assigns all the original colors to the vertexes that are part of the PLBlob object.
  *
  * @param texture const pointer to the IplImage object representing the image
  * @param textureCoordinates const reference to the vector containing the texture coordinates of the vertices
  * @param filteringMode the filtering mode that is used to sample the colors in the image (PL_FILTERMODE_NEAREST_NEIGHBOR or PL_FILTERMODE_BILINEAR)
  * @param textureCoordsIndicesBeginWithOne true when the texture coordinates indices begin with 1, false otherwise
  * @param bottemLeftOrigin true when the origin of the texture coordinates is bottom left, false when it is top left
  * @return true if the assignment was successful, false otherwise
  */
bool PLBlob::AssignOriginalColorValuesToVertexes( const IplImage *texture,
	const std::vector<double*> &textureCoordinates,
	PLFilteringMode filteringMode,
	bool textureCoordsIndicesBeginWithOne, bool bottemLeftOrigin )
{
	if (!texture) return false;

	if (texture->depth != IPL_DEPTH_8U || texture->nChannels != 3) return false;

	double *p_texcoord;
	double texcoord[2];

	int height = texture->height;
	int width = texture->width;
	//IplImage *texture_in_lab = cvCreateImage( cvSize( width, height ), texture->depth, texture->nChannels );

	int width_times_height = width * height;
	int step = texture->widthStep / sizeof(uchar);
	int num_channels = texture->nChannels;
	
	// TEST - LAB instead of RGB
	unsigned char *data = reinterpret_cast<unsigned char *>( texture->imageData );
	//cvCvtColor( texture, texture_in_lab, CV_BGR2Lab );
	//unsigned char *data = reinterpret_cast<unsigned char *>( texture_in_lab->imageData );

	double data_double[3];

	double texel_width = 1.0 / width;
	double texel_height = 1.0 / height;

	double half_texel_width = texel_width * 0.5;
	double half_texel_height = texel_height * 0.5;

	PLuint x = 0U;
	PLuint y = 0U;

	double v_current_texel = 0.0;
	double v_upper_neighbor = 0.0;
	double v_lower_neighbor = 0.0;

	double u_current_texel = 0.0;
	double u_right_neighbor = 0.0;
	double u_left_neighbor = 0.0;

	// borders for the cases when not all four pixels are taken into account
	// for bilinear filtering
	double x_min = texel_width * 0.5;
	double x_max = 1.0 - x_min;
	double y_min = texel_height * 0.5;
	double y_max = 1.0 - y_min;

	PLuint base_index = 0U;
	PLuint neighbor_index = 0U;

	PLuint lower_left_neighbor_index = 0U;
	PLuint lower_right_neighbor_index = 0U;
	PLuint upper_left_neighbor_index = 0U;
	PLuint upper_right_neighbor_index = 0U;

	double lower_left_texel[3] = { 0.0, 0.0, 0.0 };
	double lower_right_texel[3] = { 0.0, 0.0, 0.0 };
	double upper_left_texel[3] = { 0.0, 0.0, 0.0 };
	double upper_right_texel[3] = { 0.0, 0.0, 0.0 };

	double left_residual = 0.0;
	double right_residual = 0.0;
	double upper_residual = 0.0;
	double lower_residual = 0.0;

	double final_color_value[3] = { 0.0, 0.0, 0.0 };

	double lower_left_coef = 0.0;
	double lower_right_coef = 0.0;
	double upper_left_coef = 0.0;
	double upper_right_coef = 0.0;

	bool a = !bottemLeftOrigin; // top-left origin for texture-coords
	bool b = texture->origin == 0; // top-left origin in image

	set<PLVertex *> all_vertexes;

	// For every triangle inside the blob
	for (vector<PLTriangle*>::const_iterator triangle_it = mTriangles.begin();
		triangle_it != mTriangles.end(); triangle_it++)
	{
		// For every edge inside the Triangle
		for (vector<PLEdge*>::const_iterator edge_it = (*triangle_it)->GetEdges().begin();
			edge_it != (*triangle_it)->GetEdges().end(); edge_it++)
		{
			all_vertexes.insert( &(*edge_it)->From() );
		}
	}

// 	for (set<PLEdge*, PLEdge::PLEdgeVIPCompare>::const_iterator edge_it = mOutlineEdgeSet.begin();
// 		edge_it != mOutlineEdgeSet.end(); edge_it++)

	for (set<PLVertex*>::iterator vertex_it = all_vertexes.begin();
		vertex_it != all_vertexes.end(); vertex_it++)
	{
		// Just assign to the FROM-vertexes, because all "TOs" are the "FROMs" of other edges

		//p_texcoord = mTextureCoordinates[(*edge_it)->From().GetTexcoordIndex() - (offsetTextureCoords ? 1U : 0U)];
		p_texcoord = textureCoordinates[(*vertex_it)->GetTexcoordIndex() - (textureCoordsIndicesBeginWithOne ? 1U : 0U)];
		texcoord[U] = p_texcoord[U];
		texcoord[V] = p_texcoord[V];

		// if (a xor b) => image origin and texture coordinates origin differ
		if ((a && !b) || (!a && b) ) 
		{
			// flip the vertical axis for the texture coordinate
			texcoord[V] = 1.0 - texcoord[V];
		}

		// coordinates of the desired texel relative to the image
		x = static_cast<PLuint>( floor( texcoord[U] / texel_width ) );
		y = static_cast<PLuint>( floor( texcoord[V] / texel_height ) );

		if (texcoord[U] == 1.0) x--;
		if (texcoord[V] == 1.0) y--;

		base_index = y * step + x * num_channels;

		switch (filteringMode)
		{
			case PL_FILTERMODE_NEAREST_NEIGHBOR:
			{
// 				(*edge_it)->From().SetOriginalColor( data[base_index + RED_OFFSET],
// 					data[base_index + GREEN_OFFSET],
// 					data[base_index + BLUE_OFFSET] );

				data_double[RED] = static_cast<double>( data[base_index + RED] ) / 255.0;
				data_double[GREEN] = static_cast<double>( data[base_index + GREEN] ) / 255.0;
				data_double[BLUE] = static_cast<double>( data[base_index + BLUE] ) / 255.0;

// 				cout << "\nColor in RGB: " << data_double[RED] << " " << data_double[GREEN] << " " << data_double[BLUE] << endl;
// 				cout << "Color in RGB: " << (int)data[base_index + RED] << " " <<  (int)data[base_index + GREEN] << " " <<  (int)data[base_index + BLUE] << endl;
// 
// 				data_double[0] = static_cast<double>( data_lab[base_index + 0] ) / 255.0;
// 				data_double[1] = static_cast<double>( data_lab[base_index + 1] ) / 255.0;
// 				data_double[2] = static_cast<double>( data_lab[base_index + 2] ) / 255.0;
// 				cout << "Color in LAB: " << data_double[0] << " " << data_double[1] << " " << data_double[2] << endl;
// 				cout << "Color in LAB: " <<  (int)data_lab[base_index + 0] << " " <<  (int)data_lab[base_index + 1] << " " <<  (int)data_lab[base_index + 2] << endl;

				//(*edge_it)->From().SetOriginalColorBGR( data_double );
				(*vertex_it)->SetOriginalColorBGR( data_double );

				break;
			}

			case PL_FILTERMODE_BILINEAR:
			{
				// calculate color values for all four pixels and the
				// correspondig coefficients

				upper_right_coef = 0.0;
				upper_left_coef = 0.0;
				lower_left_coef = 0.0;
				lower_right_coef = 0.0;

				if ((texcoord[U] <= x_min && texcoord[V] <= y_min) ||
					(texcoord[U] >= x_max && texcoord[V] <= y_min) ||
					(texcoord[U] >= x_max && texcoord[V] >= y_max) ||
					(texcoord[U] <= x_min && texcoord[V] >= y_max))
				{
					// one of the corner pixels
					upper_right_coef = 1.0;

// 					upper_right_texel[RED] = data[base_index + RED_OFFSET];
// 					upper_right_texel[GREEN] = data[base_index + GREEN_OFFSET];
// 					upper_right_texel[BLUE] = data[base_index + BLUE_OFFSET];

					data_double[RED] = static_cast<double>( data[base_index + RED] ) / 255.0;
					data_double[GREEN] = static_cast<double>( data[base_index + GREEN] ) / 255.0;
					data_double[BLUE] = static_cast<double>( data[base_index + BLUE] ) / 255.0;

					memcpy( upper_right_texel, data_double, 3 * sizeof(double) );
				}
				else if (texcoord[U] <= x_min || texcoord[U] >= x_max)
				{
					// at the left edge or at the right edge
					v_upper_neighbor = (y + 1) * texel_height + half_texel_height;
					v_current_texel = v_upper_neighbor - texel_height;
					v_lower_neighbor = v_current_texel - texel_height;

					if (v_upper_neighbor - texcoord[V] < texcoord[V] - v_lower_neighbor)
					{
						// filter between the current texel and the upper neighbor
						upper_right_coef = (texcoord[V] - v_current_texel) * height;
						lower_right_coef = (v_upper_neighbor - texcoord[V]) * height;
						neighbor_index = base_index + step;
					}
					else
					{
						// filter between the current texel and the lower neighbor
						lower_right_coef = (texcoord[V] - v_lower_neighbor) * height;
						upper_right_coef = (v_current_texel - texcoord[V]) * height;
						neighbor_index = base_index - step;
					}

// 					upper_right_texel[RED] = data[neighbor_index + RED_OFFSET];
// 					upper_right_texel[GREEN] = data[neighbor_index + GREEN_OFFSET];
// 					upper_right_texel[BLUE] = data[neighbor_index + BLUE_OFFSET];

					data_double[RED] = static_cast<double>( data[neighbor_index + RED] ) / 255.0;
					data_double[GREEN] = static_cast<double>( data[neighbor_index + GREEN] ) / 255.0;
					data_double[BLUE] = static_cast<double>( data[neighbor_index + BLUE] ) / 255.0;

					memcpy( upper_right_texel, data_double, 3 * sizeof(double) );

// 					lower_right_texel[RED] = data[base_index + RED_OFFSET];
// 					lower_right_texel[GREEN] = data[base_index + GREEN_OFFSET];
// 					lower_right_texel[BLUE] = data[base_index + BLUE_OFFSET];

					data_double[RED] = static_cast<double>( data[base_index + RED] ) / 255.0;
					data_double[GREEN] = static_cast<double>( data[base_index + GREEN] ) / 255.0;
					data_double[BLUE] = static_cast<double>( data[base_index + BLUE] ) / 255.0;

					memcpy( lower_right_texel, data_double, 3 * sizeof(double) );
				}
				else if (texcoord[V] <= y_min || texcoord[V] >= y_max)
				{
					// at the lower edge or at the upper edge

					u_right_neighbor = (x + 1) * texel_width + half_texel_width;
					u_current_texel = u_right_neighbor - texel_width;
					u_left_neighbor = u_current_texel - texel_width;

					if (u_right_neighbor - texcoord[U] < texcoord[U] - u_left_neighbor)
					{
						// filter between the current texel and the right neighbor
						upper_right_coef = (texcoord[U] - u_current_texel) * width;
						upper_left_coef = (u_right_neighbor - texcoord[U]) * width;
						neighbor_index = base_index + num_channels;
					}
					else
					{
						// filter between the current texel and the left neighbor
						upper_left_coef = (texcoord[U] - u_left_neighbor) * width;
						upper_right_coef = (u_current_texel - texcoord[U]) * width;
						neighbor_index = base_index - num_channels;
					}

// 					upper_right_texel[RED] = data[neighbor_index + RED_OFFSET];
// 					upper_right_texel[GREEN] = data[neighbor_index + GREEN_OFFSET];
// 					upper_right_texel[BLUE] = data[neighbor_index + BLUE_OFFSET];

					data_double[RED] = static_cast<double>( data[neighbor_index + RED] ) / 255.0;
					data_double[GREEN] = static_cast<double>( data[neighbor_index + GREEN] ) / 255.0;
					data_double[BLUE] = static_cast<double>( data[neighbor_index + BLUE] ) / 255.0;

					memcpy( upper_right_texel, data_double, 3 * sizeof(double) );

// 					upper_left_texel[RED] = data[base_index + RED_OFFSET];
// 					upper_left_texel[GREEN] = data[base_index + GREEN_OFFSET];
// 					upper_left_texel[BLUE] = data[base_index + BLUE_OFFSET];

					data_double[RED] = static_cast<double>( data[base_index + RED] ) / 255.0;
					data_double[GREEN] = static_cast<double>( data[base_index + GREEN] ) / 255.0;
					data_double[BLUE] = static_cast<double>( data[base_index + BLUE] ) / 255.0;

					memcpy( upper_left_texel, data_double, 3 * sizeof(double) );
				}
				else
				{
					// somewhere in the middle
					// sample all four neighbors

					u_right_neighbor = (x + 1) * texel_width + half_texel_width;
					u_current_texel = u_right_neighbor - texel_width;
					u_left_neighbor = u_current_texel - texel_width;

					v_upper_neighbor = (y + 1) * texel_height + half_texel_height;
					v_current_texel = v_upper_neighbor - texel_height;
					v_lower_neighbor = v_current_texel - texel_height;

					if (v_upper_neighbor - texcoord[V] < texcoord[V] - v_lower_neighbor)
					{
						// take the upper neighbors
						lower_residual = texcoord[V] - v_current_texel;
						upper_residual = v_upper_neighbor - texcoord[V];

						if (u_right_neighbor - texcoord[U] < texcoord[U] - u_left_neighbor)
						{
							// take the upper right neighbors
							right_residual = u_right_neighbor - texcoord[U];
							left_residual = texcoord[U] - u_current_texel;

							upper_left_neighbor_index = base_index + step;
							upper_right_neighbor_index = base_index + step + num_channels;
							lower_left_neighbor_index = base_index;
							lower_right_neighbor_index = base_index + num_channels;
						}
						else
						{
							// take the upper left neighbors
							right_residual = u_current_texel - texcoord[U];
							left_residual = texcoord[U] - u_left_neighbor;

							upper_left_neighbor_index = base_index + step - num_channels;
							upper_right_neighbor_index = base_index + step;
							lower_left_neighbor_index = base_index - num_channels;
							lower_right_neighbor_index = base_index;
						}
					}
					else
					{
						// take the lower neighbors
						lower_residual = texcoord[V] - v_lower_neighbor;
						upper_residual = v_current_texel - texcoord[V];

						if (u_right_neighbor - texcoord[U] < texcoord[U] - u_left_neighbor)
						{
							// take the lower right neighbors
							right_residual = u_right_neighbor - texcoord[U];
							left_residual = texcoord[U] - u_current_texel;

							upper_left_neighbor_index = base_index;
							upper_right_neighbor_index = base_index + num_channels;
							lower_left_neighbor_index = base_index - step;
							lower_right_neighbor_index = base_index - step + num_channels;
						}
						else
						{
							// take the lower left neighbors
							right_residual = u_current_texel - texcoord[U];
							left_residual = texcoord[U] - u_left_neighbor;

							upper_left_neighbor_index = base_index - num_channels;
							upper_right_neighbor_index = base_index;
							lower_left_neighbor_index = base_index - step - num_channels;
							lower_right_neighbor_index = base_index - step;
						}
					}

					// The coefficients
					upper_right_coef = width_times_height * left_residual * lower_residual;
					upper_left_coef = width_times_height * right_residual * lower_residual;
					lower_left_coef = width_times_height * right_residual * upper_residual;
					lower_right_coef = width_times_height * left_residual * upper_residual;

					// Upper left neighbor
// 					upper_left_texel[RED] = data[upper_left_neighbor_index + RED_OFFSET];
// 					upper_left_texel[GREEN] = data[upper_left_neighbor_index + GREEN_OFFSET];
// 					upper_left_texel[BLUE] = data[upper_left_neighbor_index + BLUE_OFFSET];

					data_double[RED] = static_cast<double>( data[upper_left_neighbor_index + RED] ) / 255.0;
					data_double[GREEN] = static_cast<double>( data[upper_left_neighbor_index + GREEN] ) / 255.0;
					data_double[BLUE] = static_cast<double>( data[upper_left_neighbor_index + BLUE] ) / 255.0;

					memcpy( upper_left_texel, data_double, 3 * sizeof(double) );

					// Upper right neighbor
// 					upper_right_texel[RED] = data[upper_right_neighbor_index + RED_OFFSET];
// 					upper_right_texel[GREEN] = data[upper_right_neighbor_index + GREEN_OFFSET];
// 					upper_right_texel[BLUE] = data[upper_right_neighbor_index + BLUE_OFFSET];

					data_double[RED] = static_cast<double>( data[upper_right_neighbor_index + RED] ) / 255.0;
					data_double[GREEN] = static_cast<double>( data[upper_right_neighbor_index + GREEN] ) / 255.0;
					data_double[BLUE] = static_cast<double>( data[upper_right_neighbor_index + BLUE] ) / 255.0;

					memcpy( upper_right_texel, data_double, 3 * sizeof(double) );

					// Lower left neighbor
// 					lower_left_texel[RED] = data[lower_left_neighbor_index + RED_OFFSET];
// 					lower_left_texel[GREEN] = data[lower_left_neighbor_index + GREEN_OFFSET];
// 					lower_left_texel[BLUE] = data[lower_left_neighbor_index + BLUE_OFFSET];

					data_double[RED] = static_cast<double>( data[lower_left_neighbor_index + RED] ) / 255.0;
					data_double[GREEN] = static_cast<double>( data[lower_left_neighbor_index + GREEN] ) / 255.0;
					data_double[BLUE] = static_cast<double>( data[lower_left_neighbor_index + BLUE] ) / 255.0;

					memcpy( lower_left_texel, data_double, 3 * sizeof(double) );

					// Lower right neighbor
// 					lower_right_texel[RED] = data[lower_right_neighbor_index + RED_OFFSET];
// 					lower_right_texel[GREEN] = data[lower_right_neighbor_index + GREEN_OFFSET];
// 					lower_right_texel[BLUE] = data[lower_right_neighbor_index + BLUE_OFFSET];

					data_double[RED] = static_cast<double>( data[lower_right_neighbor_index + RED] ) / 255.0;
					data_double[GREEN] = static_cast<double>( data[lower_right_neighbor_index + GREEN] ) / 255.0;
					data_double[BLUE] = static_cast<double>( data[lower_right_neighbor_index + BLUE] ) / 255.0;

					memcpy( lower_right_texel, data_double, 3 * sizeof(double) );
				}

				// Final Red
				final_color_value[RED] =
					lower_left_coef * lower_left_texel[RED] +
					lower_right_coef * lower_right_texel[RED] +
					upper_left_coef * upper_left_texel[RED] +
					upper_right_coef * upper_right_texel[RED];

				// Final Green
				final_color_value[GREEN] =
					lower_left_coef * lower_left_texel[GREEN] +
					lower_right_coef * lower_right_texel[GREEN] +
					upper_left_coef * upper_left_texel[GREEN] +
					upper_right_coef * upper_right_texel[GREEN];

				// Final Blue
				final_color_value[BLUE] =
					lower_left_coef * lower_left_texel[BLUE] +
					lower_right_coef * lower_right_texel[BLUE] +
					upper_left_coef * upper_left_texel[BLUE] +
					upper_right_coef * upper_right_texel[BLUE];

				// assign the final color to the vertex
				//(*edge_it)->From().SetOriginalColorBGR( final_color_value );
				(*vertex_it)->SetOriginalColorBGR( data_double );
				break;
			}

			default:
			{
				//cvReleaseImage( &texture_in_lab );
				return false;
			}
		}
	}

	//cvReleaseImage( &texture_in_lab );
	return true;
}

/**
  *	Calculates all the outline edge paths, which are the path around the entire PLBlob region
  * and all the paths for the holes. These paths are necessary to walk around the borders
  * and calculate the vertex normals.
  */
void PLBlob::CalculateOutlineEdgePaths( void )
{
	if (mIsNull) return;

	for (vector<vector<PLEdge*>*>::iterator vector_it = mOutlineEdgePaths.begin();
		vector_it != mOutlineEdgePaths.end(); vector_it++)
	{
		delete *vector_it;
	}

	mOutlineEdgePaths.clear();

	PLEdge *current_edge;
	multiset<PLEdge*, PLEdge::PLEdgeJustFromVIPCompare>::iterator edge_it;
	
	multiset<PLEdge*, PLEdge::PLEdgeJustFromVIPCompare> outline_edges_residual( mOutlineEdgeSet.begin(), mOutlineEdgeSet.end() );

	vector<PLEdge*> *current_path_vector = new vector<PLEdge*>();
	mOutlineEdgePaths.push_back( current_path_vector );

	// Add the first edge of the path and remove it from the residuals
	edge_it = outline_edges_residual.begin();
	current_edge = *edge_it;
	current_path_vector->push_back( current_edge );
	outline_edges_residual.erase( edge_it );

	PLuint start_index = current_edge->FromVertexIndex();

	while (outline_edges_residual.size() > 0)
	{
		if (current_edge->ToVertexIndex() == start_index &&
			outline_edges_residual.find( &PLEdge( current_edge->ToVertexIndex(), 0, 0, 0, 0 ) ) == outline_edges_residual.end())
		{
			// New path in the blob, can be outline path or hole
			current_path_vector = new vector<PLEdge*>();
			mOutlineEdgePaths.push_back( current_path_vector );

			// Add the first edge of the path and remove it from the residuals
			edge_it = outline_edges_residual.begin();
			current_edge = *edge_it;
			current_path_vector->push_back( current_edge );
			outline_edges_residual.erase( edge_it );
			start_index = current_edge->FromVertexIndex();
		}
		edge_it = outline_edges_residual.find( &PLEdge( current_edge->ToVertexIndex(), 0, 0, 0, 0 ) );

		current_edge = *edge_it;
		current_path_vector->push_back( current_edge );
		outline_edges_residual.erase( edge_it );
	}
}

/**
  *	Calculates the outline triangle ring, which is the region around the entire PLBlob region
  * as well all the regions inside the holes. The outline vertexes are translated along the
  * vertex normals to find the position of the vertexes that are part of the triangle ring.
  *
  * @param widthInPixels width of the triangle ring in pixels
  * @param imageWidth width of the image
  * @param imageHeight height of the image
  * @param numVertexesInModel number of the current vertexes in the model
  * @param textureCoordinates pointer to the vector containing the texture coordinates of the vertices
  * @param textureCoordsIndicesBeginWithOne true when the texture coordinates indices begin with 1, false otherwise
  */
PLuint PLBlob::CalculateOutlineTriangleRing( const PLuint &widthInPixels,
	const PLuint &imageWidth, const PLuint &imageHeight,
	const PLuint &numVertexesInModel,
	std::vector<double*> *textureCoordinates, bool textureCoordsIndicesBeginWithOne )
{
	if (mIsNull || 0 == mOutlineEdgePaths.size()) return 0;

	if (mOutlineTriangleRing.size() > 0)
	{
		for (vector<PLTriangle*>::iterator triangle_it = mOutlineTriangleRing.begin();
			triangle_it != mOutlineTriangleRing.end(); triangle_it++)
		{
			delete *triangle_it;
		}

		mOutlineTriangleRing.clear();
	}

	const double IMAGE_RATIO = static_cast<double>( imageWidth ) / imageHeight;
	const PLuint IMAGE_ID = (*(*mOutlineEdgePaths.begin())->begin())->From().GetTextureID();
	const PLuint NUM_ELEMENTS_PER_TEXCOORD = 2U;
	const PLuint TEXTURE_COORD_INDEX_OFFSET = textureCoordsIndicesBeginWithOne ? 1U : 0U;
	const double WIDTH_IN_IMAGE_SPACE = static_cast<double>(widthInPixels) / imageWidth;

	vector<vector<PLEdge*>*>::const_iterator vector_it;
	vector<PLEdge*>::const_iterator edge_it;

	const PLVertex *from = 0;
	const PLVertex *to = 0;

	PLuint from_texcoord_index = 0U;
	PLuint to_texcoord_index = 0;

	PLuint from_ring_texcoord_index = 0;
	PLuint to_ring_texcoord_index = 0;

	PLuint num_added_vertexes = 0U;
	PLuint from_ring_vertex_index = 0U;
	PLuint to_ring_vertex_index = numVertexesInModel + TEXTURE_COORD_INDEX_OFFSET - 1;

	double *from_ring_texcoord = 0;
	double *to_ring_texcoord = 0;

	vector<PLEdge*> current_triangle_edges;

	// For all paths (outline path and all the holes)
	for (vector_it = mOutlineEdgePaths.begin();
		vector_it != mOutlineEdgePaths.end(); vector_it++)
	{
		edge_it = (*vector_it)->begin();

		from = &(*edge_it)->From();

		from_texcoord_index = from->GetTexcoordIndex() - TEXTURE_COORD_INDEX_OFFSET;

		from_ring_vertex_index = to_ring_vertex_index + 1;

		from_ring_texcoord = new double[NUM_ELEMENTS_PER_TEXCOORD];

		// Copy the existing texture coordinates to be the base of the triangle ring
		memcpy( from_ring_texcoord, (*textureCoordinates)[from_texcoord_index], NUM_ELEMENTS_PER_TEXCOORD * sizeof(double) );

		// Add the normals to define the width of the triangle ring
		from_ring_texcoord[U] += WIDTH_IN_IMAGE_SPACE * (*from->GetNormal( mID ))[U];
		from_ring_texcoord[V] += IMAGE_RATIO * WIDTH_IN_IMAGE_SPACE * (*from->GetNormal( mID ))[V];

		// Add the new texture coordinate to the vector
		from_ring_texcoord_index = static_cast<PLuint>( textureCoordinates->size() ) + TEXTURE_COORD_INDEX_OFFSET;
		textureCoordinates->push_back( from_ring_texcoord );
		num_added_vertexes++;

		// For every outline edge
		for (; edge_it != (*vector_it)->end(); edge_it++)
		{
			to = &(*edge_it)->To();

			to_texcoord_index = to->GetTexcoordIndex() - TEXTURE_COORD_INDEX_OFFSET;

			to_ring_texcoord = new double[NUM_ELEMENTS_PER_TEXCOORD];

			memcpy( to_ring_texcoord, (*textureCoordinates)[to_texcoord_index], NUM_ELEMENTS_PER_TEXCOORD * sizeof(double) );

			to_ring_texcoord[U] += WIDTH_IN_IMAGE_SPACE * (*to->GetNormal( mID ))[U];
			to_ring_texcoord[V] += IMAGE_RATIO * WIDTH_IN_IMAGE_SPACE * (*to->GetNormal( mID ))[V];

			// Add the new texture coordinate to the vector
			to_ring_texcoord_index = static_cast<PLuint>( textureCoordinates->size() ) + TEXTURE_COORD_INDEX_OFFSET;
			to_ring_vertex_index = from_ring_vertex_index + 1;
			textureCoordinates->push_back( to_ring_texcoord );
			num_added_vertexes++;

			// Generate the new triangles and add them to the vector

			// First triangle, the one with the outer edge as its base
			current_triangle_edges.clear();
			current_triangle_edges.push_back( new PLEdge( from->GetVertexIndex(), from->GetTexcoordIndex(), to_ring_vertex_index, to_ring_texcoord_index, IMAGE_ID ) );
			current_triangle_edges.push_back( new PLEdge( to_ring_vertex_index, to_ring_texcoord_index, to->GetVertexIndex(), to->GetTexcoordIndex(), IMAGE_ID ) );
			current_triangle_edges.push_back( new PLEdge( to->GetVertexIndex(), to->GetTexcoordIndex(), from->GetVertexIndex(), from->GetTexcoordIndex(), IMAGE_ID ) );

			mOutlineTriangleRing.push_back( new PLTriangle( current_triangle_edges ) );

			// Second triangle
			current_triangle_edges.clear();
			current_triangle_edges.push_back( new PLEdge( from->GetVertexIndex(), from->GetTexcoordIndex(), from_ring_vertex_index, from_ring_texcoord_index, IMAGE_ID ) );
			current_triangle_edges.push_back( new PLEdge( from_ring_vertex_index, from_ring_texcoord_index, to_ring_vertex_index, to_ring_texcoord_index, IMAGE_ID ) );
			current_triangle_edges.push_back( new PLEdge( to_ring_vertex_index, to_ring_texcoord_index, from->GetVertexIndex(), from->GetTexcoordIndex(), IMAGE_ID ) );

			mOutlineTriangleRing.push_back( new PLTriangle( current_triangle_edges ) );

			// From becomes To
			from = to;
			from_ring_texcoord_index = to_ring_texcoord_index;
			from_ring_vertex_index = to_ring_vertex_index;
		}
	}

	return num_added_vertexes;
}

/**
  *	Calculates the normals of the outline vertexes.
  *
  * @param textureCoordinates const reference to the vector containing the texture coordinates of the vertices
  * @param textureCoordsIndicesBeginWithOne true when the texture coordinates indices begin with 1, false otherwise
  */
void PLBlob::CalculateOutlineVertexNormals( const std::vector<double*> &textureCoordinates,
	bool textureCoordsIndicesBeginWithOne )
{
	if (mIsNull) return;
	if (0 == mOutlineEdgePaths.size()) CalculateOutlineEdgePaths();

	vector<vector<PLEdge*>*>::const_iterator vector_it;
	vector<PLEdge*>::const_iterator edge_it;
	
	PLEdge *current_edge = 0;
	PLEdge *first_edge = 0;

	const PLuint U = 0U;
	const PLuint V = 1U;

	const PLuint TEXCOORD_OFFSET = textureCoordsIndicesBeginWithOne ? 1U : 0U;

	PLuint from_texcoord_index = 0U;
	PLuint to_texcoord_index = 0U;
 	
	PLVec2d first_edge_vector;
	PLVec2d first_edge_normal;
 	
	PLVec2d second_edge_vector;
	PLVec2d second_edge_normal;

	// For all paths (outline path and all the holes)
	for (vector_it = mOutlineEdgePaths.begin();
		vector_it != mOutlineEdgePaths.end(); vector_it++)
	{
		edge_it = (*vector_it)->begin();

		first_edge = *edge_it;
		edge_it++;

		from_texcoord_index = first_edge->FromTexcoordIndex() - TEXCOORD_OFFSET;
		to_texcoord_index = first_edge->ToTexcoordIndex() - TEXCOORD_OFFSET;

		first_edge_vector = PLVec2d( textureCoordinates[to_texcoord_index][U] - textureCoordinates[from_texcoord_index][U],
			textureCoordinates[to_texcoord_index][V] - textureCoordinates[from_texcoord_index][V] );
		first_edge_normal = first_edge_vector.GetCWNormal();
		first_edge_normal.Normalize();

		for (; edge_it != (*vector_it)->end(); edge_it++)
		{
			current_edge = *edge_it;
			from_texcoord_index = current_edge->FromTexcoordIndex() - TEXCOORD_OFFSET;
			to_texcoord_index = current_edge->ToTexcoordIndex() - TEXCOORD_OFFSET;

			second_edge_vector = PLVec2d( textureCoordinates[to_texcoord_index][U] - textureCoordinates[from_texcoord_index][U],
				textureCoordinates[to_texcoord_index][V] - textureCoordinates[from_texcoord_index][V] );
			second_edge_normal = second_edge_vector.GetCWNormal();
			second_edge_normal.Normalize();

			current_edge->From().SetNormal( (first_edge_normal + second_edge_normal).Normalize(), mID );

			first_edge_normal = second_edge_normal;
		}

		// vertex between last and first edge
		from_texcoord_index = first_edge->FromTexcoordIndex() - TEXCOORD_OFFSET;
		to_texcoord_index = first_edge->ToTexcoordIndex() - TEXCOORD_OFFSET;

		second_edge_vector = PLVec2d( textureCoordinates[to_texcoord_index][U] - textureCoordinates[from_texcoord_index][U],
			textureCoordinates[to_texcoord_index][V] - textureCoordinates[from_texcoord_index][V] );
		second_edge_normal = second_edge_vector.GetCWNormal();
		second_edge_normal.Normalize();

		first_edge->From().SetNormal( (first_edge_normal + second_edge_normal).Normalize(), mID );
	}
}

/**
  *	Deletes all the PLTriangle objects (which then destroy the PLEdge objects)
  * the PLBlob object contains.
  */
void PLBlob::DeleteData( void )
{
// 	for (std::set<PLEdge*, PLEdge::PLEdgePCompare>::iterator it = mEdgeSet.begin();
// 		it != mEdgeSet.end();)
// 	{
// 		delete *it;
// 		it = mEdgeSet.erase( it );
// 	}

	// The triangles
	for (vector<PLTriangle*>::const_iterator it = mTriangles.begin();
		it != mTriangles.end(); it++)
	{
		delete *it;
	}
	mTriangles.clear();

	// The triangles in the outer ring
	for (vector<PLTriangle*>::const_iterator it = mOutlineTriangleRing.begin();
		it != mOutlineTriangleRing.end(); it++)
	{
		delete *it;
	}
	mOutlineTriangleRing.clear();
}

/**
  *	Returns the unique ID of the PLBlob object.
  *
  * @return unique ID of the PLBlob object
  */
const PLuint &PLBlob::GetID( void ) const
{
	return mID;
}

/**
  *	Removes (but don't deletes!) the edge from outline edges, that has the same
  * vertex indices as the edge that is passed as an argument. The direction of the
  * PLEdge (which PLVertex is From and which one is To) does not matter. When no
  * such PLEdge object is found, nothing happens.
  *
  * @param edge pointer to the PLEdge object that is removed from the outline edges (when it's found)
  */
void PLBlob::RemoveOutlineEdge( PLEdge *edge )
{
	set<PLEdge*, PLEdge::PLEdgeVIPCompare>::const_iterator it = mOutlineEdgeSet.find( edge );

	if (it != mOutlineEdgeSet.end())
	{
		// delete *it;
		mOutlineEdgeSet.erase( it );
	}
}

/**
  *	Returns the edge from outline edges, that has the same
  * vertex indices as the edge that is passed as an argument. The direction of the
  * PLEdge (which PLVertex is From and which one is To) does not matter. When no
  * such PLEdge object is found, 0 is returned.
  *
  * @return pointer to the PLEdge object that has the same vertex indices as the PLEdge object that is passed as an argument, or 0 when no such PLEdge object is found
  */
PLEdge *PLBlob::GetCorrespondingEdge( PLEdge *edge )
{
	set<PLEdge*, PLEdge::PLEdgeVIPCompare>::const_iterator it = mOutlineEdgeSet.find( edge );

	if (it != mOutlineEdgeSet.end())
	{
		return *it;
	}

	return 0;
}

/**
  *	Returns a const reference to the set of outline edges
  *
  * @return const reference to the set of outline edges
  */
const set<PLEdge*, PLEdge::PLEdgeVIPCompare> &PLBlob::GetOutlineEdgeSet( void ) const
{
	return mOutlineEdgeSet;
}

/**
  *	Returns a const reference to the vector containing pointers to the PLTriangle objects that form the outline triangle ring
  *
  * @return const reference to the vector containing pointers to the PLTriangle objects that form the outline triangle ring
  */
const std::vector<PLTriangle*> &PLBlob::GetOutlineTriangleRingVector( void ) const
{
	return mOutlineTriangleRing;
}

/**
  *	Returns a const reference to the vector containing pointers to the PLTriangle objects that are part of the PLBlob region
  *
  * @return const reference to the vector containing pointers to the PLTriangle objects that are part of the PLBlob region
  */
const std::vector<PLTriangle*> &PLBlob::GetTriangleVector( void ) const
{
	return mTriangles;
}

/**
  *	Returns true when the PLBlob object contains an outline edge that has the same vertex indices
  * as the one that is passed as an argument. The direction of the PLEdge (which PLVertex is From
  * and which one is To) does not matter.
  *
  * @param edge pointer to the PLEdge object that is in demand
  * @return true when a PLEdge object with the same vertex indices as the one that is passed as an argument is found in the outline edges, false otherwise
  */
bool PLBlob::HasOutlineEdge( PLEdge *edge ) const
{
	if (mOutlineEdgeSet.find( edge ) == mOutlineEdgeSet.end())
		return false;

	return true;
}

/**
  *	Returns true when the PLBlob object contains a vertex with the vertexIndex, that is passed as an argument,
  * false otherwise
  *
  * @param vertexIndex vertex index that is in demand
  * @return true when the PLBlob object contains a vertex with the vertexIndex, that is passed as an argument, false otherwise
  */
bool PLBlob::HasOutlineVertexIndex( const PLuint &vertexIndex ) const
{
	if (mOutlineVertexesIndexes.find( vertexIndex ) == mOutlineVertexesIndexes.end())
		return false;

	return true;
}

/**
  *	Merges the PLBlob passed as an argument into the current one. All the triangles
  * and outline edges are merged. Only the pointers are inserted, no data structures are copied.
  *
  * @param blob const reference to the PLBlob object whose triangles and outline edges are merged
  */
void PLBlob::Merge( const PLBlob &blob )
{
	// The outline edges
// 	for (set<PLEdge*, PLEdge::PLEdgeVIPCompare>::const_iterator it = blob.mEdgeSet.begin();
// 		it != blob.mEdgeSet.end(); it++)
// 	{
// 		mEdgeSet.insert( *it );
// 	}

	mOutlineEdgeSet.insert( blob.mOutlineEdgeSet.begin(), blob.mOutlineEdgeSet.end() );

	// The triangles
// 	for (vector<PLTriangle*>::const_iterator it = blob.mTriangles.begin();
// 		it != blob.mTriangles.end(); it++)
// 	{
// 		mTriangles.push_back( *it );
// 	}

	mTriangles.insert( mTriangles.end(), blob.mTriangles.begin(), blob.mTriangles.end() );
}

/**
  *	Updates all the outline vertex indexes, which means that the indexes of all the outline vertexes
  * of the PLBlob object are stored in a separate data structure. This is done for performance
  * reasons, so that the HasOutlineVertexIndex( const PLuint & ) method can check faster whether
  * the considered outline vertex is part of the PLBlob object or not.
  */
void PLBlob::UpdateOutlineVertexesIndexes( void )
{
	mOutlineVertexesIndexes.clear();

	for (set<PLEdge*, PLEdge::PLEdgeVIPCompare>::const_iterator it = mOutlineEdgeSet.begin();
		it != mOutlineEdgeSet.end(); it++)
	{
		// Add just the FROMs, because all the TOs are the FROMs of other edges
		mOutlineVertexesIndexes.insert( (*it)->FromVertexIndex() );

		// mOutlineVertexesIndexes.insert( (*it)->ToVertexIndex() );
	}
}

/**
  *	Returns the number of PLBlob objects that are currently instantiated
  */
const PLuint &PLBlob::GetNumBlobs( void )
{
	return numBlobs;
}